#include <iostream>
#include <memory>
#include <pthreads/pthread.h>
#include <string>

using namespace std;

class Thread
{
public:
    Thread();
    virtual ~Thread();
    void start();
    void* join();
private:
    // thread ID
    pthread_t mThreadID;
    pthread_attr_t threadAttribute;

    Thread(const Thread&);
    const Thread& operator=(const Thread&);

    // called when run() completes
    void setCompleted();
    // stores return value from run()
    void* result;

    virtual void* run() {return NULL;}

    static void* startThread(void* pVoid);
    void printError(std::string msg, int status);
};

Thread::Thread()
{
}

void* Thread::startThread(void* pVoid)
{
    // thread start function when no Runnable is involved
    Thread* aThread = static_cast<Thread*>(pVoid);

    aThread->result = aThread->run();
    aThread->setCompleted();
    return aThread->result;
}

Thread::~Thread() {}

void Thread::start()
{
    // initialize attribute object
    int status = pthread_attr_init(&threadAttribute);
    if(status)
    {
        printError("pthread_attr_init failed at", status);
        exit(status);
    }

    // set the scheduling scope attribute
    status = pthread_attr_setscope(&threadAttribute,
                                   PTHREAD_SCOPE_SYSTEM);
    if(status)
    {
        printError("pthread_attr_setscope failed at", status);
        exit(status);
    }

    status = pthread_create(&mThreadID, &threadAttribute,
                            Thread::startThread, (void*)this);
    if(status)
    {
        printError("pthread_create failed at", status);
        exit(status);
    }

    status = pthread_attr_destroy(&threadAttribute);
    if(status)
    {
        printError("pthread_attr_destroy failed at", status);
        exit(status);
    }
}


void* Thread::join()
{
    // A thread calling T.join() waits until thread T completes.
    int status = pthread_join(mThreadID, NULL);
    // result was already saved by thread start function
    if(status)
    {
        printError("pthread_join failed at", status);
        exit(status);
    }
    return result;
}

void Thread::setCompleted()
{
// completion handled by pthread_join()
}

void Thread::printError(std::string msg, int status)
{
    cout << msg << " - " << status << endl;
}


// shared variable
class communicatingThread: public Thread
{
public:
    communicatingThread(int ID,pthread_mutex_t *pMutex) :
        myID(ID),mpMutex(pMutex) {}
    virtual void* run();

    void Log(std::string str)
    {
        pthread_mutex_lock (mpMutex);
        cout<<str<<endl;
        pthread_mutex_unlock (mpMutex);
    }
private:
    int myID;
    pthread_mutex_t *mpMutex;
};

void* communicatingThread::run()
{
    for (int i = 0; i < 5; i++)
    {
        Log("tick");
    }
    return 0;
}

int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);

    communicatingThread *thread1 = new communicatingThread(1,&mutex);
    //auto_ptr<communicatingThread> thread2(new communicatingThread(2));
    thread1->start();
    //thread2->start();
    for(int i=0;i<8;i++)
    {
        thread1->Log("Main");
    }
    thread1->join();

    delete thread1;

    pthread_mutex_destroy(&mutex);
    return 0;
}
